﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace SnClient
{
    public interface ILogging
    {
        void LogOneEvent( LoggingEventId id, string info );
        void LogOneEvent( LoggingEventId id, EventLogEntryType type, string info );

        void LogException( Exception e, string info );
        void LogException( Exception e, long id, string info );
        void LogException( Exception e, LoggingEventId id, string info );
    }

    public enum LoggingEventId
    {
        EVENT_ID_REPORTINSTALLEDPACKAGES = 1000,

        // GENERIC
        EVENT_ID_PACKAGEREMOVALSUCCESS = 1000,
        EVENT_ID_PACKAGEREMOVALFAILURE = 2000,
        EVENT_ID_HTTPREQUEST = 6000,
        EVENT_ID_EXCEPTION = 10000,

    }

    public class Logging : ILogging
    {
        private static string EVENT_SOURCE = "SnPlugin-JWSecure";

        public void LogOneEventEx( int id, int type, string info )
        {
            EventLogEntryType t = EventLogEntryType.Error;

            if (Enum.IsDefined(typeof(EventLogEntryType), type))
                t = (EventLogEntryType)type;

            EventLog el = Create();

            var ei = new EventInstance((long)id, 0, t);
            var insert = new Object[1];

            insert[0] = info;
            el.WriteEvent(ei, insert);
        }
                
        static private EventLog Create()
        {            
            return new EventLog("" /*EVENT_LOG*/, ".", EVENT_SOURCE);
        }

        private void LogOneEventInternal( long id, int type, string info )
        {
            EventLogEntryType t = EventLogEntryType.Error;

            if (Enum.IsDefined(typeof(EventLogEntryType), type))
                t = (EventLogEntryType)type;

            this.LogOneEvent( id, t, info );
        }
        
        public void LogOneEvent( LoggingEventId id, EventLogEntryType type, string info )
        {
            LogOneEvent((long)id, type, info);           
        }

        public void LogOneEvent( LoggingEventId id, string info )
        {
            LogOneEvent((long)id, EventLogEntryType.Information, info);
        }

        public void LogOneEvent( long id, EventLogEntryType type, string info )
        {
            EventLog el = Create();

            var ei = new EventInstance((long)id, 0, type);
            var insert = new Object[1];

            insert[0] = info;
            el.WriteEvent(ei, insert);
        }

        public void LogException( Exception e, string info )
        {
            EventLog el = Create();

            String msg = BuildExceptionString(e, info);

            var ei = new EventInstance((long)LoggingEventId.EVENT_ID_EXCEPTION, 0, EventLogEntryType.Error);
            var insert = new Object[1];

            insert[0] = msg;
            el.WriteEvent(ei, insert);
        }

        static private String BuildExceptionStringInternal(Exception e)
        {
            StringBuilder ret = new StringBuilder();
            ret.Append(BuildSingleExceptionString(e));
            if (e.InnerException != null)
                ret.Append(BuildSingleExceptionString(e.InnerException));

            return ret.ToString();
        }
        
        static private String BuildSingleExceptionString(Exception e)
        {            
            if (e is SEHException)
            {
                SEHException seh = (SEHException)e;
                return String.Format("\nException: {0} {2}\n\n{1}\n", e.Message, e.StackTrace, seh.HResult);               
            }

            return String.Format("\nException: {0}\n\n{1}\n", e.Message, e.StackTrace);
        }


        static private String BuildExceptionString(Exception e, String info)
        {
            StringBuilder sb = new StringBuilder(info);
            if (e is AggregateException)
            {
                AggregateException ae = (AggregateException)e;
                foreach (Exception inner in ae.InnerExceptions)
                    sb.Append(BuildExceptionStringInternal(inner));
            }
            else
            {
                sb.Append(BuildExceptionStringInternal(e));
            }

            return sb.ToString();
        }

        public void LogException( Exception e, long id, string info )
        {
            EventLog el = Create();
            String msg = BuildExceptionString(e, info);

            var ei = new EventInstance(id, 0, EventLogEntryType.Error);
            var insert = new Object[1];

            insert[0] = msg;
            el.WriteEvent(ei, insert);
        }

        public void LogException( Exception e, LoggingEventId id, string info )
        {
            this.LogException( e, ( long )id, info );
        }  
    }
}
